﻿namespace Sage.Data.Mask
{
    /// <summary>
    /// Aho-Corasick 算法实现的敏感词匹配器
    /// </summary>
    internal class AhoCorasickMatcher
    {
        private class TrieNode
        {
            public Dictionary<char, TrieNode> Children { get; } = [];
            public TrieNode? Failure { get; set; }
            public List<string> Output { get; } = [];
        }

        private readonly TrieNode _root;

        public AhoCorasickMatcher(IEnumerable<string> patterns)
        {
            _root = new TrieNode();
            BuildTrie(patterns);
            BuildFailureLinks();
        }

        /// <summary>
        /// 构建 Trie 树
        /// </summary>
        private void BuildTrie(IEnumerable<string> patterns)
        {
            foreach (var pattern in patterns)
            {
                if (string.IsNullOrEmpty(pattern)) continue;

                var current = _root;
                foreach (var ch in pattern)
                {
                    if (!current.Children.TryGetValue(ch, out TrieNode? value))
                    {
                        value = new TrieNode();
                        current.Children[ch] = value;
                    }
                    current = value;
                }
                current.Output.Add(pattern);
            }
        }

        /// <summary>
        /// 构建失败指针（AC 自动机核心）
        /// </summary>
        private void BuildFailureLinks()
        {
            var queue = new Queue<TrieNode>();

            // 初始化根节点的直接子节点
            foreach (var child in _root.Children.Values)
            {
                child.Failure = _root;
                queue.Enqueue(child);
            }

            // BFS 构建失败指针
            while (queue.Count > 0)
            {
                var current = queue.Dequeue();

                foreach (var (ch, child) in current.Children)
                {
                    queue.Enqueue(child);

                    var failure = current.Failure;
                    while (failure != null && !failure.Children.ContainsKey(ch))
                    {
                        failure = failure.Failure;
                    }

                    child.Failure = failure?.Children.GetValueOrDefault(ch) ?? _root;

                    // 将失败节点的输出添加到当前节点
                    child.Output.AddRange(child.Failure.Output);
                }
            }
        }

        /// <summary>
        /// 查找所有匹配的敏感词及其位置
        /// </summary>
        public List<(int Position, string Word)> FindMatches(string text)
        {
            if (string.IsNullOrEmpty(text))
                return [];

            var matches = new List<(int Position, string Word)>();
            var current = _root;

            for (int i = 0; i < text.Length; i++)
            {
                var ch = text[i];

                // 寻找匹配的转移
                while (current != _root && !current.Children.ContainsKey(ch))
                {
                    current = current.Failure!;
                }

                if (current.Children.TryGetValue(ch, out TrieNode? value))
                {
                    current = value;
                }

                // 检查是否有匹配的模式
                foreach (var word in current.Output)
                {
                    matches.Add((i - word.Length + 1, word));
                }
            }

            return matches;
        }

        /// <summary>
        /// 过滤敏感词，替换为星号
        /// </summary>
        public string FilterSensitiveWords(string content)
        {
            if (string.IsNullOrEmpty(content))
                return content;

            var matches = FindMatches(content);
            if (matches.Count == 0)
                return content;

            // 按位置排序，处理重叠情况
            matches.Sort((a, b) => a.Position.CompareTo(b.Position));

            var result = content.ToCharArray();
            var processed = new bool[content.Length];

            foreach (var (position, word) in matches)
            {
                // 检查是否已被处理（避免重叠替换）
                bool canReplace = true;
                for (int i = position; i < position + word.Length && i < processed.Length; i++)
                {
                    if (processed[i])
                    {
                        canReplace = false;
                        break;
                    }
                }

                if (canReplace)
                {
                    for (int i = position; i < position + word.Length && i < result.Length; i++)
                    {
                        result[i] = '*';
                        processed[i] = true;
                    }
                }
            }

            return new string(result);
        }
    }
}
